package versionx

import (
	"gitee.com/zhongguo168a/gocodes/datax"
	"gitee.com/zhongguo168a/gocodes/datax/convertx"
	"gitee.com/zhongguo168a/gocodes/datax/floatx"
	"gitee.com/zhongguo168a/gocodes/datax/mapx"
	"sync"
)

// 独立用
func NewObject() (obj *Object) {
	obj = &Object{}
	obj.Source = NewSource()
	obj.data = obj.Source.origin
	obj.versionPaths = append(obj.versionPaths, "")
	obj.temps = datax.M{}
	return obj
}

type Object struct {
	*Source
	// 当前管理的数据
	data datax.M
	// 字段的路径
	path string
	// 在父对象中的字段名字
	field string
	// 父数据
	parent datax.M
	// 父对象的路径集合
	versionPaths []string
	// 缓存的map/array/struct等对象, 避免重复创建对象
	temps datax.M
	//
	creators map[string]func(obj IObject) IObject
	creator  func(obj IObject) IObject

	mutex sync.RWMutex
}

// 字段的路径
func (s *Object) RefFieldPath() string {
	return s.path
}

// 实例是否发生变化
func (s *Object) RefFieldChanged() bool {
	return s.versions[s.path] > s.lastVersion
}
func (s *Object) RefArray(field string) datax.A {
	s.mutex.Lock()
	val, has := s.data[field]
	if has == false {
		val = make(datax.A, 0, 0)
		s.data[field] = val
	}
	s.mutex.Unlock()
	return val.(datax.A)
}

func (s *Object) RefData() datax.M {
	return s.data
}

func (s *Object) RefGet(field string) (val interface{}) {
	s.mutex.RLock()
	val = s.data[field]
	s.mutex.RUnlock()
	return
}

func (s *Object) RefHas(field string) bool {
	s.mutex.RLock()
	_, has := s.data[field]
	s.mutex.RUnlock()
	return has
}

func (s *Object) RefUint(field string) uint {
	return uint(s.RefInt(field))
}

func (s *Object) RefInt(field string) int {
	s.mutex.Lock()
	val, has := s.data[field]
	if has == false {
		val = int(convertx.AnyToInt64(val))
		s.data[field] = val
	}
	s.mutex.Unlock()
	return val.(int)
}

func (s *Object) RefBool(field string) bool {
	s.mutex.Lock()
	val, has := s.data[field]
	if has == false {
		val = convertx.AnyToBool(val)
		s.data[field] = val
	}
	s.mutex.Unlock()
	return val.(bool)
}

func (s *Object) RefFloat64(field string) float64 {
	s.mutex.Lock()
	val := s.refFloat64(field)
	s.mutex.Unlock()
	return val
}

func (s *Object) refFloat64(field string) float64 {
	val, has := s.data[field]
	if has == false {
		val = convertx.AnyToFloat64(val)
		s.data[field] = val
	}
	return val.(float64)
}

func (s *Object) RefString(field string) string {
	s.mutex.Lock()
	val, has := s.data[field]
	if has == false {
		val = convertx.AnyToString(val)
		s.data[field] = val
	}
	s.mutex.Unlock()
	return val.(string)
}

func (s *Object) RefMap(field string) (r IObject) {
	s.mutex.Lock()
	iobj, hastemp := s.temps[field]
	if hastemp == false {
		obj := &Object{}
		obj.Source = s.Source
		obj.parent = s.data
		obj.field = field
		obj.path = s.path + "/" + field
		obj.versionPaths = append(s.versionPaths, obj.path)
		obj.temps = datax.M{}
		//
		m, has := s.data[field]
		if has {
			obj.data = m.(datax.M)
		}

		creator, hascreator := s.creators[field]
		if hascreator {
			obj.creator = creator
		}
		iobj = obj
		s.temps[field] = obj
	}
	s.mutex.Unlock()
	//
	r = iobj.(IObject)
	return
}

func (s *Object) RefClass(field string) IObject {
	s.mutex.Lock()
	iobj, hastemp := s.temps[field]
	if hastemp == false {
		obj := &Object{}
		obj.Source = s.Source
		obj.parent = s.data
		obj.field = field
		obj.path = s.path + "/" + field
		obj.versionPaths = append(s.versionPaths, obj.path)
		obj.temps = datax.M{}
		//
		m, has := s.data[field]
		if has {
			obj.data = m.(datax.M)
		}

		if s.creator != nil { // 只有map对象会有此属性
			iobj = s.creator(obj)
		} else {
			//
			creator, hascreator := s.creators[field]
			if hascreator {
				iobj = creator(obj)
			} else {
				iobj = obj
			}
		}

		s.temps[field] = iobj
	}
	s.mutex.Unlock()
	return iobj.(IObject)
}

func (s *Object) RefClassCreator(field string, creator func(object IObject) IObject) {
	if s.creators == nil {
		s.creators = map[string]func(obj IObject) IObject{}
	}
	s.mutex.Lock()
	s.creators[field] = creator
	s.mutex.Unlock()
}

// 0-无法比较 1-浮点数比较 2-其他比较
func (s *Object) compareKind(val interface{}) int {
	switch val.(type) {
	case int:
		return 2
	case int8:
		return 2
	case int16:
		return 2
	case int32:
		return 2
	case int64:
		return 2
	case uint:
		return 2
	case uint8:
		return 2
	case uint16:
		return 2
	case uint32:
		return 2
	case uint64:
		return 2
	case float32:
		return 1
	case float64:
		return 1
	case string:
		return 2
	case bool:
		return 2
	}

	return 0
}

// 只能设置基本类型
func (s *Object) RefSet(field string, val interface{}) {
	s.mutex.Lock()
	s.refSet(field, val)
	s.mutex.Unlock()
}

func (s *Object) refSet(field string, val interface{}) {
	if s.data == nil {
		panic("RefSet: data is nil")
	}
	switch s.compareKind(val) {
	case 1:
		a := s.refFloat64(field)
		if floatx.Eq(a, val.(float64)) {
			return
		}
	case 2:
		old := s.data[field]
		if old == val { // 没有变化
			return
		}
	}
	s.data[field] = val
	if s.enableVersion {
		s.keyVersionNew(s.versionPaths, s.path+"/"+field)
	}

	if s.onChange != nil {
		s.onChange(s.path, field, val)
	}
	return
}

// 设置成nil
func (s *Object) RefSetNil() {
	s.mutex.Lock()
	s.parent[s.field] = nil
	s.data = nil
	s.temps = nil

	// 不能删除，因为如果删除了，就没法同步删除map的场景
	//delete(s.parent, s.field)
	if s.enableVersion {
		s.keyVersionNew(s.versionPaths, s.path)
	}
	s.mutex.Unlock()
	//
	if s.onChange != nil {
		s.onChange(s.path, s.field, nil)
	}
}

func (s *Object) RefRemove(field string) {
	s.mutex.Lock()
	s.data[field] = nil
	delete(s.data, field)
	_, has := s.temps[field]
	if has {
		delete(s.temps, field)
	}
	s.mutex.Unlock()
	return
}

// 新建结构, 旧的数据清除
// 返回自身，便于链式调用
func (s *Object) RefNew() IObject {
	s.mutex.Lock()
	s.refNew()
	s.mutex.Unlock()
	return s
}

func (s *Object) refNew() IObject {
	m := datax.M{}
	s.parent[s.field] = m
	s.data = m
	s.temps = datax.M{}
	if s.enableVersion {
		s.keyVersionNew(s.versionPaths, s.path)
	}
	return s
}

// 如果是空的才新建结构
// 返回自身，便于链式调用
func (s *Object) RefNewIfNil() IObject {
	if s.data == nil {
		return s.RefNew()
	}
	return s
}

func (s *Object) RefIsNil() bool {
	return s.data == nil
}

func (s *Object) RefLength() int {
	return len(s.data)
}

func (s *Object) RefKeys() (r []string) {
	if s.RefIsNil() {
		return
	}
	s.mutex.RLock()
	for key := range s.data {
		r = append(r, key)
	}
	s.mutex.RUnlock()
	return
}

func (s *Object) RefReset(data map[string]interface{}) {
	if s.RefIsNil() {
		return
	}
	s.mutex.Lock()
	s.refNew()
	for key, val := range data {
		s.refSet(key, val)
	}
	s.mutex.Unlock()
	return
}

func (s *Object) RefUpdate(data map[string]interface{}) {
	if data == nil {
		s.RefSetNil()
		return
	}
	s.RefNewIfNil()
	//
	s.mutex.Lock()
	dataObject := s.data
	temps := s.temps
	for key, val := range data {
		if val == nil {
			temps[key] = nil
			dataObject[key] = nil
		} else {
			switch v := val.(type) {
			case map[string]interface{}:
				tempVal, has := s.temps[key]
				if has {
					if tempVal != nil {
						tempVal.(IObject).RefUpdate(v)
					} else {
						dataObject[key] = v
					}
				} else {
					dataVal := dataObject[key]
					if dataVal == nil {
						dataVal = map[string]interface{}{}
						dataObject[key] = dataVal
					}
					mapx.Copy(v, dataVal.(map[string]interface{}))
				}
			default:
				s.refSet(key, val)
			}
		}
	}
	s.mutex.Unlock()
	return
}

// 把对象脱离与Root的关联，自己独立成Root，可以避免运行时对Root产生影响
func (s *Object) RefRemoveFromRoot() {
	source := NewSourceWith(s.data)
	s.Source = source

	s.mutex.Lock()
	s.parent[s.field] = nil
	s.parent = nil
	s.versionPaths = s.versionPaths[:0]
	s.mutex.Unlock()

}
